﻿using System;
using System.Linq;
using System.Web.Mvc;

using Fuse8.DomainFramework.Common;

namespace Fuse8.ContentManagementFramework.Web.ApplicationRuntime
{
    public class CmsViewEngine : IViewEngine
    {
        #region Properties

        public IViewEngine BaseViewEngine { get; protected set; }

        public Func<ControllerContext, bool> ViewEngineScopeChecker { get; protected set; }

        public string PathToSearch { get; protected set; }

        #endregion

        #region Constructor

        public CmsViewEngine(Func<ControllerContext, bool> viewEngineScopeChecker, VirtualPathProviderViewEngine baseViewEngine, string location, string pathToSearch)
        {
            Guard.ArgumentNotNull(viewEngineScopeChecker, "viewEngineScopeChecker");
            Guard.ArgumentNotNull(baseViewEngine, "baseViewEngine");

            if (!string.IsNullOrWhiteSpace(location))
            {
                Func<string, string> viewLocationConverter =
                    (p) =>
                    {
                        return !string.IsNullOrWhiteSpace(p) ?
                            p.Replace("~", string.Format("~/{0}", location)) : p;
                    };

                if (baseViewEngine.MasterLocationFormats != null)
                {
                    baseViewEngine.MasterLocationFormats = baseViewEngine.MasterLocationFormats.Select(viewLocationConverter).ToArray();
                }

                if (baseViewEngine.AreaMasterLocationFormats != null)
                {
                    baseViewEngine.AreaMasterLocationFormats = baseViewEngine.AreaMasterLocationFormats.Select(viewLocationConverter).ToArray();
                }

                if (baseViewEngine.ViewLocationFormats != null)
                {
                    baseViewEngine.ViewLocationFormats = baseViewEngine.ViewLocationFormats.Select(viewLocationConverter).ToArray();
                }

                if (baseViewEngine.AreaViewLocationFormats != null)
                {
                    baseViewEngine.AreaViewLocationFormats = baseViewEngine.AreaViewLocationFormats.Select(viewLocationConverter).ToArray();
                }

                baseViewEngine.PartialViewLocationFormats = baseViewEngine.ViewLocationFormats;
                baseViewEngine.AreaPartialViewLocationFormats = baseViewEngine.AreaViewLocationFormats;
            }
            
            BaseViewEngine = baseViewEngine;
            ViewEngineScopeChecker = viewEngineScopeChecker;
            PathToSearch = pathToSearch;            
        }

        #endregion

        #region IViewEngine Members

        public ViewEngineResult FindPartialView(ControllerContext controllerContext, string partialViewName, bool useCache)
        {
            ViewEngineResult result = null;

            if (ViewEngineScopeChecker(controllerContext))
            {
                result = BaseViewEngine.FindPartialView(
                    controllerContext,
                    !string.IsNullOrWhiteSpace(PathToSearch) ?
                        string.Format("{0}/{1}", PathToSearch, partialViewName) :
                        partialViewName,
                    useCache);
            }

            return result ?? new ViewEngineResult(new string[] { });
        }

        public ViewEngineResult FindView(ControllerContext controllerContext, string viewName, string masterName, bool useCache)
        {
            ViewEngineResult result = null;

            if (ViewEngineScopeChecker(controllerContext))
            {
                result = BaseViewEngine.FindView(
                    controllerContext,
                    !string.IsNullOrWhiteSpace(PathToSearch) ?
                        string.Format("{0}/{1}", PathToSearch, viewName) :
                        viewName,
                    masterName,
                    useCache);
            }

            return result ?? new ViewEngineResult(new string[] { });
        }

        public void ReleaseView(ControllerContext controllerContext, IView view)
        {
            IDisposable disposable = view as IDisposable;
            if (disposable != null)
            {
                disposable.Dispose();
            }
        }

        #endregion
    }

    public static class CmsViewEngineExtensions
    {
        public static bool UserAgentContains(this ControllerContext context, string agentToFind)
        {
            Guard.ArgumentNotNull(context, "context");
            Guard.ArgumentNotEmpty(agentToFind, "agentToFind");

            return context.HttpContext.Request.UserAgent.IndexOf(agentToFind, StringComparison.OrdinalIgnoreCase) > 0;
        }

        public static bool IsInAdministrationArea(this ControllerContext context)
        {
            return IsInArea(context, "/admin");
        }

        public static bool IsInArea(this ControllerContext context, string area)
        {
            Guard.ArgumentNotNull(context, "context");
            Guard.ArgumentNotEmpty(area, "area");

            return context.HttpContext.Request.RawUrl.StartsWith(area, StringComparison.OrdinalIgnoreCase);
        }

        public static bool IsMobileDevice(this ControllerContext context)
        {
            Guard.ArgumentNotNull(context, "context");

            return context.HttpContext.Request.Browser.IsMobileDevice;
        }

        public static void AddCustom<T>(this ViewEngineCollection viewEngineCollection, Func<ControllerContext, bool> viewEngineScopeChecker, string location)
            where T : VirtualPathProviderViewEngine, new()
        {
            AddCustom<T>(viewEngineCollection, viewEngineScopeChecker, location, string.Empty);
        }
        
        public static void AddCustom<T>(this ViewEngineCollection viewEngineCollection, Func<ControllerContext, bool> viewEngineScopeChecker, string location, string pathToSearch)
            where T : VirtualPathProviderViewEngine, new()
        {
            Guard.ArgumentNotNull(viewEngineCollection, "viewEngineCollection");
            Guard.ArgumentNotNull(viewEngineScopeChecker, "viewEngineScopeChecker");

            viewEngineCollection.Add(new CmsViewEngine(viewEngineScopeChecker, new T(), location, pathToSearch));
        }

        public static void AddForBrowser<T>(this ViewEngineCollection viewEngineCollection, string userAgentSubstring, string pathToSearch)
            where T : VirtualPathProviderViewEngine, new()
        {
            AddForBrowser<T>(viewEngineCollection, userAgentSubstring, string.Empty, pathToSearch);
        }

        public static void AddForBrowser<T>(this ViewEngineCollection viewEngineCollection, string userAgentSubstring, string location, string pathToSearch)
            where T : VirtualPathProviderViewEngine, new()
        {
            Guard.ArgumentNotNull(viewEngineCollection, "viewEngineCollection");
            Guard.ArgumentNotEmpty(userAgentSubstring, "userAgentSubstring");
            Guard.ArgumentNotEmpty(pathToSearch, "pathToSearch");

            viewEngineCollection.Add(new CmsViewEngine(p => p.UserAgentContains(userAgentSubstring), new T(), location, pathToSearch));
        }
    }
}
